package com.wh1200.mybatis.xmlless.model.dto

import cn.hutool.core.util.ReflectUtil
import com.wh1200.mybatis.xmlless.AnnotatedElementDetailInfo
import com.wh1200.mybatis.xmlless.ElementInfoBuilder
import com.wh1200.mybatis.xmlless.annotations.*
import com.wh1200.mybatis.xmlless.model.FieldMappingList
import com.wh1200.mybatis.xmlless.model.XmllessTableInfo
import com.wh1200.mybatis.xmlless.model.base.BaseMethodWrapper
import com.wh1200.mybatis.xmlless.model.base.BaseXmllessMethodWrapper
import com.wh1200.mybatis.xmlless.model.criteria.ParameterInfo
import com.wh1200.mybatis.xmlless.resolver.MappingResolver
import com.wh1200.mybatis.xmlless.resolver.ParameterResolver
import com.wh1200.mybatis.xmlless.resolver.TypeResolver
import com.wh1200.mybatis.xmlless.util.ClassWrapperProvider
import com.wh1200.mybatis.xmlless.util.TableInfoProvider
import com.wh1200.mybatis.xmlless.util.hasAnnotation
import org.apache.ibatis.annotations.ResultMap
import java.lang.reflect.Method

/**
 *
 * @author 吴昊
 * @date 2024/1/17 21:21
 * @since v0.0.0
 * @version 1.0
 */
class DefaultMethodWrapper(
    private val method: Method,
    private val mapperClass: Class<*>,
    private val modelClass: Class<*>,
    val tableInfo: XmllessTableInfo
) : BaseMethodWrapper {

  override fun resolveReturnMappingList(): FieldMappingList? {
    val wrapper = ClassWrapperProvider.createClassWrapper(TypeResolver.resolveReturnMappingType(method, mapperClass))
    return if (wrapper.getClassName() != modelClass.name
        && !this.isJsonResult()
        && wrapper.isComplex()
    ) {
      val currentTableInfo = TableInfoProvider.getOrInitXmllessTableInfo(wrapper)
      MappingResolver.resolveNonEntityClass(
          wrapper, modelClass.name, tableInfo, currentTableInfo
      )
    } else {
      null
    }
  }

  override fun getReturnType(): Class<*> {
    return TypeResolver.resolveReturnMappingType(method, mapperClass)
  }

  override fun getParameters(): List<ParameterInfo> {
    return method.parameters.map {
      val detail = ElementInfoBuilder.build(
          DefaultParameterWrapper(it)
      )
      ParameterResolver.buildParameterInfo(detail)
    }
  }

  override fun getXmllessMethod(): BaseXmllessMethodWrapper {
    return DefaultXmllessMethodWrapper(method.getAnnotation(XmllessMethod::class.java))
  }

  override fun getPropertiesMappingInner(): Map<String, String> {
    val returnType = TypeResolver.resolveReturnMappingType(method, mapperClass)
    return if (returnType != Void.TYPE) {
      ReflectUtil.getFields(returnType).filter { it.isAnnotationPresent(PropertyMapping::class.java) }
          .associate {
            it.name to it.getAnnotation(PropertyMapping::class.java).value
          }
    } else {
      mapOf()
    }
  }

  override fun isJsonResult(): Boolean {
    return method.hasAnnotation("com.wh1200.mybatis.xmlless.annotations.JsonResult")
  }

  override fun getResultMap(): List<String> {
    return method.getAnnotation(ResultMap::class.java)?.value?.toList() ?: listOf()
  }

  override fun getFullName(): String {
    return mapperClass.name + "." + method.name
  }

  override fun getExcludeProperties(): List<String> {
    return method.getAnnotation(ExcludeProperties::class.java)?.value?.toList() ?: listOf()
  }

  override fun getSelectedProperties(): List<String> {
    return method.getAnnotation(SelectedProperties::class.java)?.value?.toList() ?: listOf()
  }

  override fun getName(): String {
    return method.name
  }

  override fun isLogicDisabled(): Boolean {
    return method.isAnnotationPresent(DisableLogic::class.java)
  }

  override fun getOptionalParameterNames(): Array<String> {
    return ParameterResolver.resolveNames(method)
  }

  override fun getModelFields(): List<AnnotatedElementDetailInfo> {
    return ReflectUtil.getFields(modelClass).toList().map {
      ElementInfoBuilder.build(
          DefaultFieldWrapper(it)
      )
    }
  }

  override fun getModelName(): String {
    return modelClass.name
  }

}
